from MineRandom import MineShuffle
from Setting import GridInfo


class Director:
    def __init__(self, set):
        self.set = set
        self.monitor = None
        self.croupier = None
        self.reporter = None
        self.referee = None
        #self.t = 0

    def start(self, action = []):

        self.monitor = self.createMonitor()
        self.croupier = self.createCroupier()
        self.reporter = self.createReporter()
        self.referee = self.createReferee()
        #self.t = 0
        if action != []:
            self.set.first_click = action

        shuffle_list = self.croupier.shuffleMine()
        base = self.croupier.getBase()
        self.monitor.setBoard(shuffle_list, base, 1)
        # self.monitor.printBoard()
        self.monitor.calBoard()

        # self.monitor.printBoard()

    def direct(self, x, y):
        if not self.reporter.isClickable(x, y):
            return []

        if not self.takeAction(x, y):
            self.referee.setFlag()

        grid_list = self.reporter.getList()
        return grid_list

    def preform(self, x, y):
        print(f"drag start {x} {y}")

        if not self.reporter.isClicked(x, y):
            return []

        print(f"drag x:{x} y:{y} ")

        if not self.takeAction(x, y, 0):

            self.referee.setFlag()

        print(f"drag success")

        grid_list = self.reporter.getList()
        return grid_list

    def isEnd(self):
        return self.referee.isEnd()

    def isSuccess(self):
        return self.referee.isSuccess()

    def isPlantable(self, x, y):
        return self.reporter.isClose(x, y)

    def plantFlag(self, x, y):
        return GridInfo(x, y, self.reporter.changeBoardCell(x, y), True)

    def calVal(self, x, y):
        return self.reporter.calValue(x, y)

    def takeAction(self, x, y, mod=1):  # 用之前保证[x][y]为未知格(mod为1)

        #self.t += 1
        self.reporter.clear()
        # print(f"x:{x} y:{y} value:{self.monitor.getNum(x, y)}")
        if self.monitor.getNum(x, y) == 9:
            self.reporter.setList(self.monitor.mine_info)
            return False

        queue = [[x, y]]
        count = 0

        if mod != 1:
            queue.pop(0)
            if self.reporter.flagNum(x, y)==self.monitor.getNum(x, y):
                if self.checkFlag(x,y):
                    search_list = self.reporter.safeSearch(x, y)
                    for item in search_list:
                        queue.append(item)
                else:
                    self.reporter.setList(self.monitor.mine_info)
                    return False
            else:
                return True
        else:
            self.reporter.openBoardCell(x, y)

        while len(queue) > 0:

            i0 = queue[0][0]
            j0 = queue[0][1]
            queue.pop(0)

            if self.monitor.isMine(i0, j0) == 1:
                continue
            elif self.monitor.isBlank(i0, j0):
                self.reporter.append(i0, j0, 0)
                search_list = self.reporter.search(i0, j0)
                for item in search_list:
                    queue.append(item)
                count += 1
            else:
                self.reporter.append(i0, j0, self.monitor.getNum(i0, j0))
                count += 1

        self.referee.addCount(count)
        return True

    def checkFlag(self,x,y):
        for i in range(max(0, x - 1), min(x + 2, self.set.height)):
            for j in range(max(0, y - 1), min(y + 2, self.set.width)):
                if self.monitor.isMine(i,j):
                    if not self.reporter.isFlag(i,j):
                        return False
                elif self.reporter.isFlag(i,j):
                    return False
        return True

    def createMonitor(self):
        width = self.set.width
        height = self.set.height
        mine_count = self.set.mine_count
        first_click = self.set.first_click

        mine_board = []

        for i in range(height + 2):
            board_row = []
            for j in range(width + 2):
                board_row.append(0)
            mine_board.append(board_row)

        return Monitor(width, height, mine_count, mine_board, first_click)

    def createCroupier(self):
        width = self.set.width
        height = self.set.height

        base = max(width, height) + 1

        s_list = []

        for i in range(height):
            for j in range(width):
                s_list.append(i * base + j)

        return Croupier(base, s_list)

    def createReporter(self):
        width = self.set.width
        height = self.set.height

        view_board = []

        for i in range(height):
            board_row0 = []
            for j in range(width):
                board_row0.append(0)
            view_board.append(board_row0)

        return Reporter(width, height, view_board)

    def createReferee(self):
        return Referee(self.set.mine_count, self.set.height * self.set.width)


class Monitor:
    def __init__(self, width, height, mine_count, mine_board, first_click=[]):
        self.width = width
        self.height = height
        self.mine_count = mine_count
        self.mine_board = mine_board
        self.first_click = first_click
        self.mine_info = []
        # self.cal_board=mine_board

    def printBoard(self):
        for row in self.mine_board:
            print(row)

    def setMine(self, i, j):
        # print(f'{i} {j}')
        # self.printBoard()
        # print("stop")
        # print(f"{self.mine_board[i][j + 1]}")
        self.mine_board[i + 1][j + 1] = 9
        # print(f"{self.mine_board[i][j+1]}")
        # self.printBoard()
        self.mine_info.append(GridInfo(i, j, 9, False))

    def setBoard(self, s_list, base, distance=0):
        l = len(s_list)
        count = 0
        i = self.first_click[0]
        j = self.first_click[1]

        for index in range(l):
            i0 = s_list[index] // base
            j0 = s_list[index] % base

            if i0 >= (i - distance) and i0 <= (i + distance):
                if j0 >= (j - distance) and j0 <= (j + distance):
                    continue

            count += 1
            self.setMine(i0, j0)
            # self.printBoard()

            # print(f"{count}")
            if count == self.mine_count:
                break

    def getNum(self, x, y):
        return self.mine_board[x + 1][y + 1]

    def isMine(self, i, j):
        if self.mine_board[i + 1][j + 1] != 9:
            return 0
        return 1

    def isBlank(self, i, j):
        if self.mine_board[i + 1][j + 1] == 0:
            return True
        return False

    def calCell(self, i, j):
        self.mine_board[i + 1][j + 1] = self.isMine(i - 1, j - 1) + self.isMine(i - 1, j) + self.isMine(i - 1,
                                                                                                        j + 1) + self.isMine(
            i, j - 1) + self.isMine(i, j + 1) + self.isMine(i + 1, j - 1) + self.isMine(i + 1, j) + self.isMine(i + 1,
                                                                                                                j + 1)

        # print(f"{self.mine_board[i+1][j+1]}")

    def calBoard(self):
        # self.printBoard()
        for i in range(self.height):
            for j in range(self.width):
                if self.mine_board[i + 1][j + 1] != 9:
                    self.calCell(i, j)


class Croupier:
    def __init__(self, base, s_list):
        self.base = base
        self.shuffle_controller = MineShuffle(s_list)

    def shuffleMine(self):
        return self.shuffle_controller.shuffle()

    def getList(self, n=0):
        return self.shuffle_controller.getShuffle(n)

    def getBase(self):
        return self.base


class Reporter:
    def __init__(self, width, height, view_board):
        self.width = width
        self.height = height
        self.view_board = view_board
        self.report_list = []

    def beExist(self,x,y):
        if x>-1 and x<self.height:
            if y>-1 and y<self.width:
                return True

        return False

    def getBoard(self):
        return self.view_board

    def printBoard(self):
        for row in self.view_board:
            print(row)

    def getBoardCell(self, x, y):
        return self.view_board[x][y]

    def openBoardCell(self, x, y):
        self.view_board[x][y] = 1

    def changeBoardCell(self, x, y):
        if self.view_board[x][y] == 0 or self.view_board[x][y] == -1:
            self.view_board[x][y] = self.view_board[x][y] - 1
        elif self.view_board[x][y] == -2:
            self.view_board[x][y] = 0
        return 10 - self.view_board[x][y]

    def clear(self):
        self.report_list.clear()

    def append(self, x, y, status, flag=False):
        report = GridInfo(x, y, status, flag)
        self.report_list.append(report)

    def setList(self, r_list):
        for r_row in r_list:
            self.report_list.append(r_row)

    def getList(self):
        return self.report_list

    def search(self, x, y):
        search_list = []
        for i in range(max(0, x - 1), min(x + 2, self.height)):
            for j in range(max(0, y - 1), min(y + 2, self.width)):
                if self.isClose(i, j):
                    search_list.append([i, j])
                    self.openBoardCell(i, j)
        return search_list

    def safeSearch(self, x, y):
        search_list = []
        for i in range(max(0, x - 1), min(x + 2, self.height)):
            for j in range(max(0, y - 1), min(y + 2, self.width)):
                if self.isClose(i, j) and not self.isFlag(i,j):
                    search_list.append([i, j])
                    self.openBoardCell(i, j)
        return search_list

    def isFlag(self, x, y):
        return self.view_board[x][y] == -1

    def isClicked(self, x, y):
        return self.view_board[x][y] == 1

    def isClose(self, x, y):
        return self.getBoardCell(x, y) <= 0

    def isClickable(self, x, y):
        return self.view_board[x][y] == 0

    def calValue(self, x, y):
        if self.view_board[x][y] == -1:
            return -1
        elif self.view_board[x][y] == -2:
            return 1
        return 0

    def flagNum(self, x, y):
        search_count = 0

        for i in range(max(0, x - 1), min(x + 2, self.height)):
            for j in range(max(0, y - 1), min(y + 2, self.width)):
                if self.isFlag(i, j):
                    search_count += 1

        return search_count

    def subNum(self, x1, y1, v1, x2, y2, v2):
        dx = x2 - x1
        dy = y2 - y1
        area1 = []
        area2 = []

        for i in [-1, 0 ,1]:
            xx1 = x1 - dx + dy * i
            yy1 = y1 - dy + dx * i

            if self.beExist(xx1,yy1):
                if self.isFlag(xx1,yy1):
                    v1 -= 1
                elif self.isClose(xx1,yy1):
                    area1.append((xx1,yy1))

            xx2 = x2 + dx + dy * i
            yy2 = y2 + dy + dx * i

            if self.beExist(xx2,yy2):
                if self.isFlag(xx2,yy2):
                    v2 -= 1
                elif self.isClose(xx2,yy2):
                    area2.append((xx2,yy2))

        if v2 - v1 == len(area2):
            return (area1, area2)
        elif v1 -v2 == len(area1):
            return (area2, area1)

        return ([], [])

    def unknownNum(self, x, y, val):
        """
        配合第一策略第一阶段
        :param x:
        :param y:
        :param val:
        :return:
        """
        search_count = 0
        search_list = []

        for i in range(max(0, x - 1), min(x + 2, self.height)):
            for j in range(max(0, y - 1), min(y + 2, self.width)):
                if self.isClickable(i, j):
                    search_count += 1
                    search_list.append((i, j))
                elif self.isFlag(i, j):
                    search_count += 1

        if val != search_count:
            return [-1, -1]
        else:
            for (x,y) in search_list:
                self.changeBoardCell(x,y)
        return search_list

    def blankNum(self, x, y, val):
        """
        配合第一策略第二阶段
        :param x:
        :param y:
        :param val:
        :return:
        """
        search_count = 0
        search_list = []

        for i in range(max(0, x - 1), min(x + 2, self.height)):
            for j in range(max(0, y - 1), min(y + 2, self.width)):
                if self.isClickable(i, j):
                    search_list.append((i, j))
                elif self.isFlag(i, j):
                    search_count += 1

        if val != search_count:
            return [-1, -1]

        return search_list


class Referee:
    def __init__(self, mine_count, total_count):
        self.count = 0
        self.mine_count = mine_count
        self.total_count = total_count
        self.flag = False

    def isSuccess(self):
        if self.total_count - self.count == self.mine_count:
            return True
        return False

    def isEnd(self):
        return self.flag or self.isSuccess()

    def getCount(self):
        return self.count

    def addCount(self, n):
        self.count += n

    def setFlag(self):
        self.flag = True
